'''
Created on Nov 25, 2009

@author: anch
'''
import copy
import array
import exception_def as ex
import gamemodel.piece as mPiece
import random, math
import interface.ConsoleAgent as itfc
import db.DbAgent as db


class Neighbor55(object):
	'''
	classdocs
	'''
	__default_mapping = {(2, 4): 10, (1, 3): 7, (3, 3): 8, (2, 2): 5, (0, 4): 9, (4, 2): 6, (3, 1): 3, (1, 1): 2, (4, 4): 11, (0, 2): 4}
	__default_reverse_mapping = [None, None, (1, 1), (3, 1), (0, 2), (2, 2), (4, 2), (1, 3), (3, 3), (0, 4), (2, 4), (4, 4)]
	__allowable_focused_piece = [(0,0), (2, 0), (4, 0)]
	
	__transformation =[
		[
			[(2, 0), (4, 0), (1, 1), (3, 1), (0, 2), (2, 2), (4, 2), (1, 3), (3, 3), (0, 4), (2, 4), (4, 4)],
			[(2, 0), (4, 0), (1, 1), (3, 1), (0, 2), (2, 2), (4, 2), (1, 3), (3, 3), (0, 4), (2, 4), (4, 4)]
		], #0
	
		[
			[(0, 0), (4, 0), (1, 1), (3, 1), (0, 2), (2, 2), (4, 2), (1, 3), (3, 3), (0, 4), (2, 4), (4, 4)],
			[(-2, 0), (2, 0), (-1, 1), (1, 1), (-2, 2), (0, 2), (2, 2), (-1, 3), (1, 3), (-2, 4), (0, 4), (2, 4)]
		], #1
	
		[
			[(0, 0), (2, 0), (1, 1), (3, 1), (0, 2), (2, 2), (4, 2), (1, 3), (3, 3), (0, 4), (2, 4), (4, 4)],
			[(-4, 0), (-2, 0), (-3, 1), (-1, 1), (-4, 2), (-2, 2), (0, 2), (-3, 3), (-1, 3), (-4, 4), (-2, 4), (0, 4)]
		], #2
	]
	'''
		the second line of each group is the coordinates shifting for x0, y0
	'''
	
	def __init__(self):
		'''
		Constructor
		'''
		self.__data = [None] * 12
		self.__focus = 0 # 0 for left lower corner
						# 1 for middle bottom
						# 2 for right lower corner
		self.moves = [None] * 4
		self.mapping = copy.copy(NS.__default_mapping)
		self.reverse_mapping = copy.copy(NS.__default_reverse_mapping)
	
	def __eq__(self, b):
		if b == None or not isinstance(b, Neighbor55):
			return False
		
		return self.__data == b.__data \
				and self.reverse_mapping == b.reverse_mapping
	
	def setByCoord(self, x, y, piece, ref_player = None):
		'''
		@piece:
			0 : empty
			1 : self
			2 : opponent
		'''
		if piece == None:
			piece = 0
		elif isinstance(piece, mPiece.CheckerPiece) and ref_player != None:
			if piece.player != ref_player:
				piece = 2
			else:
				piece = 1
		elif not piece in (0, 1, 2):
			raise ex.ExInvalidParameter
		
		if (x, y) == NS.__allowable_focused_piece[self.__focus]:
			raise RuntimeError("Cannot set the focused position")
		
		data_solt = self.mapping.get((x,y))
		if data_solt == None:
			return
		
		self.__data[data_solt] = piece
	
	def getByCoord(self, x, y):
		'''
		return:
			0 : empty
			1 : self
			2 : opponent
		'''
		if (x, y) == NS.__allowable_focused_piece[self.__focus]:
			return 1
		
		data_solt = self.mapping.get((x, y))
		if data_solt == None:
			return 0
		
		return self.__data[data_solt]
	
	def getMoveProb(self, move):
		'''
			@move: 
					0 : Move A
					1 : Move B
					2 : Move C
					3 : Move D
		'''
		if not move in [0, 1, 2, 3]:
			raise ex.ExInvalidParameter
		
		if move == 3 and self.__focus != 1:
			raise ex.ExInvalidParameter
		
		return self.moves[move]
	
	@staticmethod
	def construct_skeleton(focus):
		'''
		@focus:
			# 0, or (0,0) for left lower corner
			# 1, or (0,2) for middle bottom
			# 2, or (0,4) for right lower corner
		'''
		data_solts = copy.copy(NS.__allowable_focused_piece)
		
		if focus in NS.__allowable_focused_piece:
			focus = data_solts.index(focus)
			del data_solts[focus]
			pass
		elif focus in (0, 1, 2):
			del data_solts[focus]
			pass
		else:
			raise ex.ExInvalidParameter
		
		instance = NS()
		instance.mapping.update(
			zip(data_solts, range(2))				
		)
		instance.reverse_mapping[0:2] = data_solts
		instance.__focus = focus
		
		return instance
	
	@staticmethod
	def __constructImportData(instance, state, player, x0, y0):
		
		trans = NS.__transformation[instance.__focus]
		list_t, list_o, l = trans[0], trans[1], len(trans[0])
		board = state.getBoard()
		
		for i in range(l):
			x = x0 + list_o[i][0]
			y = y0 + list_o[i][1]
			
			if x < 0 or x > 9 or y < 0 or y > 9:
				#wall encountered
				instance.setByCoord(list_t[i][0], list_t[i][1], 0)
				continue
			
			piece = board.getBoardPosition(x, y, player)
			if piece == None:
				piece = 0
			elif piece.player != player:
				piece = 2
			else:
				piece = 1
			
			instance.setByCoord(list_t[i][0], list_t[i][1], piece)
	
	def __loadProbabilities(self):
		db_item = db.DbAgent.getNormalPieceProbs(self.encode())
		
		if self.__focus == 1: # middle focus, four moves
			_range = 4
		else:
			_range = 3
			
		for i in range(_range):
			self.moves[i] = db_item.probs[i]
		pass
	
	def __normalize(self):
		i = 0
		sum = 0
		for e in self.moves:
			if e != 0 and e != None:
				i+=1
				sum += e
		
		for j, e in enumerate(self.moves):
			if e != None:
				self.moves[j] = float(e) / sum * 100
				
		pass
	
	@staticmethod
	def constructFromState(state, x0, y0, ref_player = None):
		'''
			@state	: the state to be converted into three sub-neighborhood
			@x0		: the focused piece x coordinate, based on which we create a
						sub-neighborhood
			@y0		: the focused piece y coordinate, based on which we create a
						sub-neighborhood
			@ref_player: the player used to judge whether a piece is: 
					a self piece, or an opponent piece
		'''
		r = [None] * 3
		
		if x0 < 8 and y0 < 8:
			#sub case one
			r[0] = NS.construct_skeleton((0,0))
			pass
		
		if x0 > 1 and x0 < 8 and y0 < 8:
			#sub case two
			r[1] = NS.construct_skeleton((2,0))
			pass
		
		if x0 > 3 and y0 < 8:
			map = lambda x, y, x0, y0: (x0 - (4 - x), y0)
			#sub case three
			r[2] = NS.construct_skeleton((4,0))
			pass
		
		for i, e in enumerate(r):
			if e != None:
				NS.__constructImportData(e, state, ref_player, x0, y0)
				r[i] = e
				e.__loadProbabilities()
				e.__normalize()
		
		return r
			
	def encode(self):
		data = self.__buildDataCopy()
		#24 + 1 bits
		bytes = array.array('B', [0] * 4)
		i, j, s = 0, 0, 6
		while j != 12:
			bytes[i] = bytes[i] ^ (data[j] << s)
			j+=1
			s-=2
			if s < 0:
				i+=1
				s = 6
		
		mode = self.__focus
		if mode == 2:
			mode = 0
		bytes[3] = mode
		
		return bytes.tostring()
	
	def __buildDataCopy(self):
		if self.__focus == 2:
			#mirroring
			data = [None] * 12
			for key, value in self.mapping.items():
				x, y = 4 - key[0], key[1]
				data[value] = self.getByCoord(x, y)
		else:
			data = self.__data
		return data		
	
	def draw(self):
		itfc.ConsoleDescriptor.drawNeighborhood(self)
		pass
					
NS = Neighbor55

#dic = dict()
#i , j = 0, 2
#
#for y in range(1,5):
#	for x in range(5):
#		if i != 0 :
#			dic.update({(x, y): j})
#			j+=1
#		i = 1 - i
#		
#		pass
#	
#print repr(dic)
#
#lis = [None] * 12
#for key, value in dic.iteritems() :
#	lis[value] = key
#	
#print repr(lis)


def generateMapping(f_x, f_y):
	lis1 = []
	diff = []
	i , j = 0, 2
	for y in range(5):
		for x in range(5):
			i = 1 - i
			if x == f_x and y == f_y:
				continue
			if i == 1:
				lis1.append((x,y))
				diff.append((x - f_x, y - f_y))
	
	print repr(lis1)
	print repr(diff)
	
#generateMapping(2, 0)
	